/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  coroutine.h
 *         \unit  coroutine
 *        \brief  This is a C language coroutine library
 *       \author  Lamdonn
 *      \version  v0.2.0
 *      \license  GPL-2.0
 *    \copyright  Copyright (C) 2025 Lamdonn.
 ********************************************************************************************************/
#ifndef __coroutine_H
#define __coroutine_H

#ifdef __cplusplus
extern "C" {
#endif

/* Version infomation */
#define COROUTINE_V_MAJOR                           0
#define COROUTINE_V_MINOR                           2
#define COROUTINE_V_PATCH                           0

#include "coroutine_cfg.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <setjmp.h>

/**
 * \brief: Coroutine task
 */
typedef struct CoTask*                              CoTask_t;

/**
 * \brief: Coroutine timer
 */
typedef struct CoTimer*                             CoTimer_t;

/**
 * \brief: Coroutine task entry function
 * \param arg: Task argument address
 * \return: Task return value address
 */
typedef void *(*CoTaskEntry_t)(void *arg);

/**
 * \brief: Coroutine timer entry function
 */
typedef void (*CoTimerEntry_t)(void);

/**
 * \brief: Coroutine lock function
 * \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, lock
 */
typedef void (*CoLock_t)(void);

/**
 * \brief: Coroutine unlock function
 * \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, unlock
 */
typedef void (*CoUnlock_t)(void);

/**
 * \brief: Coroutine malloc function
 * \param size: Allocate memory size
 * \return: Allocate memory address
 * \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, malloc
 */
typedef void *(*CoMalloc_t)(size_t size);

/**
 * \brief: Coroutine free function
 * \param block: Free memory address
 * \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, free
 */
typedef void (*CoFree_t)(void *block);

/**
 * \brief: Coroutine tick function
 * \return: Current tick value
 * \note: The time base on which the coroutine schedule depends, such as system tick.
 */
typedef uint64_t (*CoTick_t)(void);

/**
 * \brief: Coroutine event
 * \note: Used for synchronization between coroutines
 */
typedef struct
{
    uint32_t flag;                              /**< Event flag, 32 bit storage, each bit can individually represent an event */
} CoEvent;

/**
 * \brief: Coroutine scheduler initialize parameter
 * \note: Coroutine scheduler initialize parameter is used to initialize the coroutine scheduler.
 */
typedef struct CoSchedulerInitPara
{
    CoLock_t lock;                              /**< Coroutine scheduler lock function */
    CoUnlock_t unlock;                          /**< Coroutine scheduler unlock function */
    CoMalloc_t malloc;                          /**< Coroutine scheduler malloc function */
    CoFree_t free;                              /**< Coroutine scheduler free function */
} CoSchedulerInitPara;

/**
 * \brief: Coroutine task create parameter
 * \note: Coroutine task create parameter is used to create a coroutine task.
 */
typedef struct CoTaskCreatePara
{
    void *pStack;                               /**< Coroutine task stack address */
    size_t stackSize;                           /**< Coroutine task stack size */
    void *arg;                                  /**< Coroutine task argument */
    int schedulerId;                            /**< Coroutine task scheduler id */
} CoTaskCreatePara;

/**
 * \brief: Coroutine timer create parameter
 * \note: Coroutine timer create parameter is used to create a coroutine timer.
 */
typedef struct CoTimerCreatePara
{
    uint64_t ms;                                /**< Coroutine timer ms, indicates the ms interval */
    uint64_t tick;                              /**< Coroutine timer tick, indicates the tick interval */
    int schedulerId;                            /**< Coroutine timer scheduler id */
} CoTimerCreatePara;

/**
 * \brief: Coroutine task wait parameter
 * \note: Coroutine task wait parameter is used to blocking task waiting for event or timeout.
 */
typedef struct CoTaskWaitPara
{
    CoEvent *pEvent;                            /**< Coroutine task wait event pointer */
    uint64_t ms;                                /**< Coroutine task wait timeout in ms */
    uint64_t tick;                              /**< Coroutine task wait timeout in tick */
} CoTaskWaitPara;

/**
 * \brief: Coroutine task create parameter default value
 * \note: Coroutine task create parameter default value is used to create a coroutine task.
 */
#define COROUTINE_E_OK                          (0)             /**< Coroutine task create parameter default value, indicates success */
#define COROUTINE_E_INVALID_PARAMETER           (-1)            /**< Coroutine task create parameter default value, indicates invalid parameter */
#define COROUTINE_E_INVALID_TICK                (-2)            /**< Coroutine task create parameter default value, indicates invalid tick function */
#define COROUTINE_E_INVALID_TICK_INTERVAL       (-3)            /**< Coroutine task create parameter default value, indicates invalid tick interval */
#define COROUTINE_E_INVALID_LOCK                (-4)            /**< Coroutine task create parameter default value, indicates invalid lock function */
#define COROUTINE_E_INVALID_MHOOK               (-5)            /**< Coroutine task create parameter default value, indicates invalid memory hook function */
#define COROUTINE_E_TCB_MEM_STACK_FAIL          (-6)            /**< Coroutine task create parameter default value, indicates memory stack fail */

/**
 * \brief: Coroutine event default value
 * \note: Coroutine event default value is used to initialize the coroutine event.
 */
#define COEVENT_STATIC_VALUE                    ((CoEvent){.flag=0})

/**
 * \brief: Coroutine scheduler initialize function
 * \param pScheduler: [Non-default parameter] Coroutine scheduler pointer
 * \param tick: [Non-default parameter] Coroutine scheduler tick function, must be specified
 * \param tickInterval: [Non-default parameter] Coroutine scheduler tick interval, indicates how many ns each tick is, must be specified
 * \param lock: [Default parameter] Coroutine scheduler lock function, 
 *              for use on multiple threads, you must specify as follows .lock=thread_lock 
 * \param unlock: [Default parameter] Coroutine scheduler unlock function, 
 *              for use on multiple threads, you must specify as follows .unlock=thread_unlock 
 * \param malloc: [Default parameter] Coroutine scheduler malloc function, 
 *              if you need to allocate memory dynamically (CoTask, CoTimer, stack, etc),
 *              you must specify as follows .malloc=mallloc
 * \param free: [Default parameter] Coroutine scheduler free function, 
 *              if you need to free memory dynamically (CoTask, CoTimer, stack, etc),
 *              you must specify as follows .free=free
 * \return: Coroutine task create parameter default value, indicates success
 * \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
 *        It must be called once before any other coroutine scheduler function.
 *        Only one initialization is allowed within a single thread
 *        It will initialize the coroutine scheduler with the given parameter.
 *        The coroutine scheduler will use the given tick function to measure the load of each task.
 *        The tick function must be called periodically with the given tick interval.
 *        The coroutine scheduler will use the given lock function to protect the shared resources.
 *        The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
 *        The coroutine scheduler will use the given free function to free memory of the tasks.
 * \warning: The coroutine scheduler will not check the validity of the given parameter.
 *           You must ensure that the given parameter is valid.
 * \example:
 *  CoScheduler_Init(0, GetTimerMs, 1000000);
 * \example:
 *  CoScheduler_Init(0, GetTimerUsec, 1000);
 * \example:
 *  CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
 * \example:
 *  CoScheduler_Init(0, GetTimerUsec, 1000, .malloc=mallloc, .free=free);
 */
#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...) CoScheduler_InitP(CoSchedulerId, tick, tickInterval, (CoSchedulerInitPara[1]){[0]={.lock=NULL,.unlock=NULL,.malloc=NULL,.free=NULL,__VA_ARGS__}})

/**
 * \brief: Coroutine scheduler initialize function
 * \param pScheduler: Coroutine scheduler pointer
 * \param tick: Coroutine scheduler tick function
 * \param tickInterval: Coroutine scheduler tick interval, indicates how many ns each tick is
 * \param pPara: Coroutine scheduler initialize parameter pointer
 * \return: Coroutine task create parameter default value, indicates success
 * \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
 *        It must be called once before any other coroutine scheduler function.
 *        Only one initialization is allowed within a single thread
 *        It will initialize the coroutine scheduler with the given parameter.
 *        The coroutine scheduler will use the given tick function to measure the load of each task.
 *        The tick function must be called periodically with the given tick interval.
 *        The coroutine scheduler will use the given lock function to protect the shared resources.
 *        The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
 *        The coroutine scheduler will use the given free function to free memory of the tasks.
 */
int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara);

/**
 * \brief: Coroutine scheduler start function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine task create parameter default value, indicates success
 * \note: Coroutine scheduler start function is used to start the coroutine scheduler.
 *        It must be called once after the coroutine scheduler is initialized.
 *        It will start the coroutine scheduler and run the tasks.
 * \example:
 *  CoScheduler_Init(0, GetTimerUsec, 1000);
 *  testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest), 0);
 *  CoScheduler_Start(0);
 */
int CoScheduler_Start(uint32_t CoSchedulerId);

/**
 * \brief: Coroutine scheduler exit function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine task create parameter default value, indicates success
 * \note: Coroutine scheduler exit function is used to exit the coroutine scheduler.
 *        It must be called once after the coroutine scheduler is started.
 *        It will exit the coroutine scheduler and stop the tasks.
 */
int CoScheduler_Exit(uint32_t CoSchedulerId);

/**
 * \brief: Coroutine scheduler task count function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine scheduler task count, indicates the number of tasks in the coroutine scheduler
 * \note: Coroutine scheduler task count function is used to get the number of tasks in the coroutine scheduler.
 */
int CoScheduler_TaskCount(uint32_t CoSchedulerId);

/**
 * \brief: Coroutine scheduler timer count function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine scheduler timer count, indicates the number of timers in the coroutine scheduler
 * \note: Coroutine scheduler timer count function is used to get the number of timers in the coroutine scheduler.
 */
int CoScheduler_TimerCount(uint32_t CoSchedulerId);

/**
 * \brief: Coroutine scheduler current load function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine scheduler current load, indicates the load of the coroutine scheduler
 * \note: Coroutine scheduler current load function is used to get the current load of the coroutine scheduler.
 *        The load is the number of ticks that the coroutine scheduler has run.
 *        The load is updated periodically with the given tick interval.
 */
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
#endif 

/**
 * \brief: Coroutine scheduler maximum load function
 * \param pScheduler: Coroutine scheduler pointer
 * \return: Coroutine scheduler maximum load, indicates the maximum load of the coroutine scheduler
 * \note: Coroutine scheduler maximum load function is used to get the maximum load of the coroutine scheduler.
 *        The load is the number of ticks that the coroutine scheduler has run.
 *        The load is updated periodically with the given tick interval.
 */
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
#endif 

/**
 * \brief: Coroutine task create function
 * \param entry: [Non-default parameter] Coroutine task entry function, indicates the entry function of the coroutine task, must be specified
 * \param stack: [Default parameter] Coroutine task stack, indicates the stack of the coroutine task.
 *              If not specified, it is allocated via the scheduler's malloc function.
 * \param stackSize: [Default parameter] Coroutine task stack size, indicates the size of the coroutine task stack
 *              If not specified, it is the default stack size `COROUTINE_STACK_DEFAULT_SIZE`.
 * \param arg: [Default parameter] Coroutine task argument, indicates the argument of the coroutine task
 * \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
 *                      Negative value means the auto distribution.
 * \return: Coroutine task handle, indicates the created coroutine task
 * \note: Coroutine task create function is used to create a coroutine task.
 *        It will create a coroutine task with the given parameter.
 *        The coroutine task will run in the coroutine scheduler.
 * \example:
 *  CoTask_t testCoroutine = CoTask_Create(test);
 * \example:
 *  CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
 * \example:
 *  CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
 * \example:
 *  int arg = 4096;
 *  CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
 */
#define CoTask_Create(entry, ...)                   CoTask_CreateP(entry, (CoTaskCreatePara[1]){[0]={.pStack=NULL,.stackSize=0,.arg=NULL,.schedulerId=-1,__VA_ARGS__}})

/**
 * \brief: Coroutine task create function, it is recommended to use @ref `CoTask_Create` instead
 * \param pPara: Coroutine task create parameter pointer
 * \return: Coroutine task handle, indicates the created coroutine task
 * \note: Coroutine task create function is used to create a coroutine task.
 *        It will create a coroutine task with the given parameter.
 *        The coroutine task will run in the coroutine scheduler.
 */
CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara);

/**
 * \brief: Coroutine task delete function
 * \param CoTask: Coroutine task handle, indicates the coroutine task to be deleted
 * \return: Coroutine task delete parameter default value, indicates success
 * \note: Coroutine task delete function is used to delete a coroutine task.
 *        It will delete the given coroutine task.
 *        The coroutine task must be in the deleted state.
 */
int CoTask_Delete(CoTask_t CoTask);

/**
 * \brief: Coroutine task self function
 * \return: Coroutine task handle, indicates the current coroutine task
 * \note: Coroutine task self function is used to get the current coroutine task.
 *        It will return the handle of the current coroutine task.
 */
CoTask_t CoTask_Self(void);

/**
 * \brief: Coroutine task scheduler id function
 * \return: Coroutine task scheduler id, indicates the scheduler id of the coroutine task
 * \note: Coroutine task scheduler id function is used to get the scheduler id of the coroutine task.
 */
int CoTask_SchedulerId(void);

/**
 * \brief: Coroutine task stack max used function
 * \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack max used
 * \return: Coroutine task stack max used, indicates the maximum stack used of the coroutine task
 * \note: Coroutine task stack max used function is used to get the maximum stack used of the coroutine task.
 *        The stack used is the maximum stack used of the coroutine task.
 *        The stack used is updated periodically with the given tick interval.
 */
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
#endif

/**
 * \brief: Coroutine task stack current used function
 * \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack current used
 * \return: Coroutine task stack current used, indicates the current stack used of the coroutine task
 * \note: Coroutine task stack current used function is used to get the current stack used of the coroutine task.
 *        The stack used is the current stack used of the coroutine task.
 *        The stack used is updated periodically with the given tick interval.
 */
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackCurUsed(CoTask_t CoTask);
#endif 

/**
 * \brief: Coroutine task wait function, blocking task waiting for event or timeout.
 * \param pEvent: [Default parameter] Coroutine event pointer, indicates the event to wait
 * \param ms: [Default parameter] Coroutine task wait ms, indicates the ms to wait
 * \param tick: [Default parameter] Coroutine task wait tick, indicates the tick to wait
 * \return: Coroutine event value, indicates the event value that triggered the coroutine task
 * \note: Coroutine task wait event function is used to wait for the given event. 
 *        @ref `CoEvent_Init` must first be called to initialize the event.
 *        @ref `CoEvent_Notify` notifies the occurrence of the event
 *        It will block the current coroutine task.
 *        The event value is a bit mask, each bit represents an event.
 *        If multiple events are triggered, the corresponding bits will be set.
 * \note: Coroutine task wait ms function is used to wait for the given ms.
 *        It will block the current coroutine task.
 * \note: Coroutine task wait tick function is used to wait for the given tick.
 *        It will block the current coroutine task.
 * \example:
 *  void *test(void *arg)
 *  {
 *      while (1)
 *      {
 *          // Give up access to the scheduler without waiting, similar to yeild
 *          CoTask_Wait();
 * 
 *          // Wait for 1000 ms
 *          CoTask_Wait(.ms=1000);
 * 
 *          // Wait for 1000000 tick
 *          CoTask_Wait(.tick=1000000);
 * 
 *          // Wait for 1000 ms or event, if no any events occur within 1000 ms, and will timeout blocking
 *          CoTask_Wait(.pEvent=&g_Event, .ms=1000);
 *          
 *          // Always wait for the event, and judge every events
 *          uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
 *          if (evs & 0x01)
 *          {
 *              printf("event 0x01 triggered\n");
 *          }
 *          if (evs & 0x02)
 *          {
 *              printf("event 0x02 triggered\n");
 *          }
 *      }
 *  }
 */
#define CoTask_Wait(...)                        CoTask_WaitP((CoTaskWaitPara[1]){[0]={.pEvent=NULL,.ms=0,.tick=0,__VA_ARGS__}})

/**
 * \brief: Coroutine task wait function, it is recommended to use @ref `CoTask_Wait` instead
 * \param pPara: Coroutine task wait parameter pointer
 * \return: Coroutine event value, indicates the event value that triggered the coroutine task
 * \note: Coroutine task wait function is used to wait for the given event or timeout.
 *        It will block the current coroutine task.
 *        The event value is a bit mask, each bit represents an event.
 *        If multiple events are triggered, the corresponding bits will be set.
 */
uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);

/**
 * \brief: Coroutine task wait functions, the version of the macro definition used is often used
 * \param m: Coroutine task wait ms, indicates the ms to wait
 * \param t: Coroutine task wait tick, indicates the tick to wait
 * \param e: Coroutine event pointer, indicates the event to wait
 * \return: Coroutine event value, indicates the event value that triggered the coroutine task
 */
#define CoTask_WaitMs(m)                        CoTask_Wait(.ms=m)
#define CoTask_WaitTick(t)                      CoTask_Wait(.tick=t)
#define CoTask_WaitEvent(e)                     CoTask_Wait(.pEvent=e)
#define CoTask_WaitEventMs(e, m)                CoTask_Wait(.pEvent=e, .ms=m)
#define CoTask_WaitEventTick(e, t)              CoTask_Wait(.pEvent=e, .tick=t)

/**
 * \brief: Coroutine timer create function
 * \param entry: [Non-default parameter] Coroutine timer entry, indicates the timer entry function
 * \param ms: [Default parameter] Coroutine timer ms, indicates the ms interval
 * \param tick: [Default parameter] Coroutine timer tick, indicates the tick interval
 * \param schedulerId: [Default parameter] Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
 *                      Negative value means the auto distribution.
 * \return: Coroutine timer handle, indicates the created timer
 * \note: One of the ms and tick parameters must be set
 * \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
 *        The timer will call the given entry function periodically with the given ms or tick interval.
 * \example:
 *  CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100); // create a timer with 100ms interval
 */
#define CoTimer_Create(entry, ...)                  CoTimer_CreateP(entry, (CoTimerCreatePara[1]){[0]={.ms=0,.tick=0,.schedulerId=-1,__VA_ARGS__}})

/**
 * \brief: Coroutine timer create function, it is recommended to use @ref `CoTimer_Create` instead
 * \param pPara: Coroutine timer create parameter pointer
 * \return: Coroutine timer handle, indicates the created timer
 * \note: Coroutine timer create function is used to create a timer with the given ms or tick interval.
 *        The timer will call the given entry function periodically with the given ms or tick interval.
 */
CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara);

/**
 * \brief: Coroutine timer create ms function, it is recommended to use @ref `CoTimer_CreateMs` instead
 * \param entry: Coroutine timer entry, indicates the timer entry function
 * \param m: Coroutine timer ms, indicates the ms interval
 * \param t: Coroutine timer tick, indicates the tick interval
 * \return: Coroutine timer handle, indicates the created timer
 * \note: Coroutine timer create ms function is used to create a timer with the given ms interval.
 *        The timer will call the given entry function periodically with the given ms interval.
 * \example:
 *  CoTimer_t timer = CoTimer_CreateMs(timer_entry, 100); // create a timer with 100ms interval
 */
#define CoTimer_CreateMs(entry, m)                  CoTimer_Create(entry, .ms=m)
#define CoTimer_CreateTick(entry, t)                CoTimer_Create(entry, .tick=t)

/**
 * \brief: Coroutine timer delete function
 * \param Timer: Coroutine timer handle, indicates the timer to delete
 * \note: Coroutine timer delete function is used to delete the given timer.
 *        It must be called after @ref `CoTimer_CreateTick` or @ref `CoTimer_CreateMs`.
 * \example:
 *  CoTimer_Delete(timer); // delete the timer
 */
void CoTimer_Delete(CoTimer_t Timer);

/**
 * \brief: Coroutine timer self function
 * \return: Coroutine timer handle, indicates the current timer
 * \note: Coroutine timer self function is used to get the handle of the current timer.
 * \example:
 *  CoTimer_t timer = CoTimer_Self(); // get the current timer
 */
CoTimer_t CoTimer_Self(void);

/**
 * \brief: Coroutine timer scheduler id function
 * \return: Coroutine timer scheduler id, indicates the scheduler id of the coroutine timer
 * \note: Coroutine timer scheduler id function is used to get the scheduler id of the coroutine timer.
 */
int CoTimer_SchedulerId(void);

/**
 * \brief: Coroutine event initialize function
 * \param pEvent: Coroutine event pointer, indicates the event to initialize
 * \note: Coroutine event initialize function is used to initialize the given event.
 *        It must be called before @ref `CoTask_WaitEvent`.
 * \example:
 *  CoEvent_Init(&g_Event);
 */
void CoEvent_Init(CoEvent *pEvent);

/**
 * \brief: Coroutine event notify function
 * \param pEvent: Coroutine event pointer, indicates the event to notify
 * \param evs: Coroutine event value, indicates the event value to notify
 * \note: Coroutine event notify function is used to notify the occurrence of the event.
 *        It will set the corresponding bits in the event value.
 * \example:
 *  CoEvent_Notify(&g_Event, 0x01); // notify event 0x01
 * \example:
 *  CoEvent_Notify(&g_Event, 0x01 | 0x02); // notify event 0x01 and 0x02
 */
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);

#ifdef __cplusplus
}
#endif

#endif // !__coroutine_H
